package cn.xawl.interview.thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author lh
 * @date 2020/08/23 15:12:30
 *
 * 1.验证volatile的可见性
 * 1.1. 假如int number = 0，number变量之前没有添加volatile关键字：main线程无法知道number被修改
 * 1.2. number 变量使用volatile关键字后，main线程可以及时知道number被修改
 *
 * 2.验证volatile的原子性
 * 2.1使用volatile修饰number。创建20个线程，每个线程对number加1000次，最终由main线程获取最终值(可以看到，最终结果不一定是目标值)
 * 2.2为什么不能保证原子性？？
 *      当多个线程同时拿到number后，其中一个线程先++后，写入主内存后，其他线程可能由于线程调度被挂起没有得到最新值，直接将主内存的number直接覆盖，导致最终值不是预期值
 * 2.3如何解决？
 *      * 加synchronized
 *      * 使用JUC下的AtomicInteger
 *
 *
 **/
public class VolatileDemo {

    public static void main(String[] args) {
        MyData myData = new MyData();
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addPP();
                    myData.addPPAtomic();
                }
            }, String.valueOf(i)).start();
        }

        //如果当前线程数多于两个线程（主线程和GC线程），就礼让，让其他线程先执行
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() + "获取到number value:" + myData.number);
        System.out.println(Thread.currentThread().getName() + "获取到atomicInteger value:" + myData.atomicInteger);

    }

    private static void volatileIsVisibility() {
        MyData myData = new MyData();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "未修改number：" + myData.number);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addTo60();
            System.out.println(Thread.currentThread().getName() + "修改number后,number = " + myData.number);
        }, "A").start();

        while (myData.number == 0) {
//            System.out.println(Thread.currentThread().getName() + "未读取到number被修改");
        }

//        System.out.println(Thread.currentThread().getName() + "读取到number被修改,number = " + myData.number);
        myData.number += 30;
        System.out.println(myData.number);
    }

}


class MyData{

    AtomicInteger atomicInteger = new AtomicInteger();
    volatile int number = 0;

    public void addTo60() {
        this.number = 60;
    }

    public synchronized void addPP() {
        number++;
    }
    public void addPPAtomic() {
        atomicInteger.getAndIncrement();
    }

}